import { Device, ActionType, TouchMeta, Banner, DeviceRotation } from "../serve/interfaces"
import { setSender, showCommand, hideCommand, CommandItem, defaultCommands } from './command'
                            
export { ActionType, defaultCommands, CommandItem }

export interface STFEaseClientProps {
    /** ws连接 */
    ws: WebSocket
    /** 改写send */
    sender?: WebSocket['send']
    /** 播放使用的canvas */
    canvas: HTMLCanvasElement
    /** 
     * 初始化canvas2d的参数
     * @default options { alpha: false }
    */
    options?: CanvasRenderingContext2DSettings
    /**
     * canvas 展示高度
     * 宽度会根据设备计算出来，建议居中展示
     */
    height?: number
    /**
     * 自定义菜单命令
     */
    commands?: CommandItem[]
    /**
     * ws连接初始化完成
     */
    onWSReady?: (ws: WebSocket) => void
    /**
     * 服务端发送截图banner信息
     */
    onBanner?: (info?: Banner) => void
    /**
     * 服务端发送设备基础信息
     */
    onDevice?: (device?: Device) => void
    /**
     * 服务端发送minitouch连接信息
     */
    onTouchMeta?: (meta?: TouchMeta) => void
    /**
     * 接收到图片
     */
    onFrame?: (frame?: Blob) => void
    /**
     * 服务端发送错误信息
     */
    onErrorInfo?: (err?: string) => void
    /**
     * ws关闭
    */
    onClose?: () => void
    /**
     * 不开启操作
     */
    readonly?: boolean
}
export default ({
    ws,
    canvas,
    options = { alpha: false },
    height = 780,
    commands = defaultCommands,
    onBanner,
    onDevice,
    onTouchMeta,
    onFrame,
    onErrorInfo = alert,
    onClose,
    sender,
    readonly,
}: STFEaseClientProps) => {
    const g = canvas.getContext('2d', options)
    canvas.height = height
    sender = sender || ws.send.bind(ws)
    setSender(sender, commands)
    ws.binaryType = 'blob'

    let banner: Banner
    let touch_meta: TouchMeta
    let device: Device
    let param_x = 0;
    let param_y = 0;
    const isTransverse = () => {
        if (device) {
            return device.rotation === DeviceRotation.R90 || device.rotation === DeviceRotation.R270
        }
    }
    const onMessage = function (e: MessageEvent) {
        if (typeof e.data === 'string') {
            const [a, type, data] = e.data.match(/^(\w+)\:(.*?)$/);
            switch (type as ActionType) {
                case ActionType.BANNER:
                    banner = JSON.parse(data);
                    onBanner && onBanner(banner)
                    break;
                case ActionType.DEVICE:
                    device = JSON.parse(data)
                    onDevice && onDevice(device)
                    break;
                case ActionType.TOUCH_META:
                    touch_meta = JSON.parse(data)
                    onTouchMeta && onTouchMeta(touch_meta)
                    break;
                case ActionType.ERROR:
                    onErrorInfo(data)
                    break;
            }
            return
        }
        if (!banner || !device || document.hidden) {
            return
        }
        let blob = new Blob([e.data], { type: 'image/jpeg' })
        onFrame && onFrame(blob)
        createImageBitmap(blob).then(function (img) {
            if (!banner) return
            if (isTransverse()) {
                canvas.width = height
                canvas.height = banner.realWidth * height / banner.realHeight;
                param_x = banner.realHeight / canvas.width
                param_y = banner.realWidth / canvas.height
            } else {
                canvas.width = banner.realWidth * height / banner.realHeight;
                canvas.height = height;
                param_x = banner.realWidth / canvas.width
                param_y = banner.realHeight / canvas.height
            }

            g.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.width * img.height / img.width);
            blob = null
        })
    }
    ws.addEventListener('message', onMessage)
    ws.addEventListener('close', function () {
        destory(),
        onClose && onClose()
    })

    let is_mousedown = false
    let mousedown: {x: number, y: number, t: number}
    let mousemove: {x: number, y: number, t: number}
    let mouseover = false
    const onMouseOver = function () {
        mouseover = true
    }
    const onKeyUp = function (e: KeyboardEvent) {
        const keymap = {
            'alt': 57,
            'shift': 59,
            'tab': 61,
            ' ': 62,
            'enter': 66,
            'backspace': 67,
        }
        if (mouseover) {
            let k = e.key.toLowerCase()
            if (keymap[k]) {
                sender(`${ActionType.COMMAND}: input keyevent ${keymap[k]}`)
            } else if (e.key.length === 1) {
                sender(`${ActionType.COMMAND}: input text "${e.key}"`)
            }
            e.preventDefault()
            e.stopPropagation()
        }
    }
    const onMouseDown = function (e: MouseEvent) {
        const { offsetX, offsetY, button } = e
        // button == 0 表示鼠标正常左键
        if (button != 0 || !banner) return
        is_mousedown = true

        if (e.target === canvas) {
            const x = (offsetX * param_x) >> 0
            const y = (offsetY * param_y) >> 0
            mousedown = {
                x, y, t: Date.now()
            }
            if (touch_meta) {
                sender(`${ActionType.TOUCH}: d 0 ${x} ${y} ${touch_meta.max_pressure}`)
            }
        }
    }
    const onMouseMove = function (e: MouseEvent) {
        const { offsetX, offsetY } = e
        if (is_mousedown && banner) {
            const x = (offsetX * param_x) >> 0
            const y = (offsetY * param_y) >> 0
            if (touch_meta) {
                sender(`${ActionType.TOUCH}: ${ mousedown ? 'm' : 'd' } 0 ${x} ${y} ${touch_meta.max_pressure}`)
            }
            mousemove = {
                x, y, t: Date.now()
            }
            if (!mousedown) {
                mousedown = mousemove
            }
        }
    }
    const onMouseUp = function () {
        if (is_mousedown && banner && mousedown) {
            if (touch_meta) {
                sender(`${ActionType.TOUCH}: u 0`)
            } else {
                mousemove = mousemove || { ...mousedown, t: Date.now () }
                const x1 = mousedown.x
                const y1 = mousedown.y
                const x2 = mousemove.x
                const y2 = mousemove.y
                sender(`${ActionType.COMMAND}: input swipe ${x1} ${y1} ${x2} ${y2} ${mousemove.t - mousedown.t}`)
            }
        }
        is_mousedown = false
        mousedown = null
        mousemove = null
    }
    const onMouseLeave = function () {
        onMouseUp()
        mouseover = false
    }
    const onContextMenu = function (e: MouseEvent) {
        e.preventDefault();
        showCommand({ target: e.target as HTMLElement})
    }
    

    let wheel_timer: NodeJS.Timeout;
    let isWheelRun = false;
    let delta = 0;
    const mousewheel = function(e) {
        const { offsetX, offsetY, deltaY } = e
        if (touch_meta && banner) {
            const x = (offsetX * param_x) >> 0
            const y = (offsetY * param_y) >> 0
            const _y = (deltaY > 0 ? 50 : -50) * param_y
            if (!isWheelRun) {
                isWheelRun = true
                sender(`${ActionType.TOUCH}: d 0 ${x} ${y} ${touch_meta.max_pressure}`)
            }
            delta += _y >> 0
            sender(`${ActionType.TOUCH}: m 0 ${x} ${y - delta} ${touch_meta.max_pressure}`)
            clearTimeout(wheel_timer)
            wheel_timer = setTimeout(() => {
                // sender(`${ActionType.TOUCH}: u 0`)
                isWheelRun = false
                delta = 0;
            }, 60);
            e.stopPropagation();
        }
    }

    if (!readonly) {
        document.addEventListener('keydown', onKeyUp)
        document.addEventListener('mousedown', onMouseDown)
        canvas.addEventListener('mouseover', onMouseOver)
        canvas.addEventListener('mousemove', onMouseMove)
        document.addEventListener('mouseup', onMouseUp)
        canvas.addEventListener('mouseleave', onMouseLeave)
        canvas.addEventListener('contextmenu', onContextMenu)
        canvas.addEventListener('wheel', mousewheel)
    }

    const destory = function () {
        if (!readonly) {
            document.removeEventListener('keydown', onKeyUp)
            document.removeEventListener('mousedown', onMouseDown)
            canvas.removeEventListener('mouseover', onMouseOver)
            canvas.removeEventListener('mousemove', onMouseMove)
            document.removeEventListener('mouseup', onMouseUp)
            canvas.removeEventListener('mouseleave', onMouseLeave)
            canvas.removeEventListener('contextmenu', onContextMenu)
            canvas.removeEventListener('wheel', mousewheel)
        }
        device = null
        banner = null
        touch_meta = null
        mouseover = false
        ws.close();
    }

    const updateHeight = (h: number) => {
        height = h
    }

    let __touch_meta: typeof touch_meta

    const handle = {
        isTouch: () => !!touch_meta,
        useAdb: () => {
            __touch_meta = touch_meta
            touch_meta = null
        },
        useTouch: () => {
            if (__touch_meta) {
                touch_meta = __touch_meta
            }
        }
    }
    return {
        setSender: (_sender: WebSocket['send']) => {
            if(readonly) {
                throw 'readonly, cannot set sender!'
            }
            sender = _sender
            setSender(_sender)
        },
        showCommand,
        hideCommand,
        updateHeight,
        destory,
        handle,
    }
}